﻿using System;
using System.Collections;

using System.ComponentModel;
using System.Linq.Expressions;
using System.Xml.Linq;
using System.Globalization;

using Caliburn.Micro;


namespace DailyFx.UI
{
	using Common;


	public class Configuration : IConfiguration, INotifyPropertyChanged
	{
		/// <summary>
		///		property fields by configuration .
		/// </summary>
		internal protected SortedList mProperties = new SortedList();

		/// <summary>
		///		logging helper .
		/// </summary>
		public static ILog mLog = LogManager.GetLog(typeof( Configuration));

		
		public IConfiguration Create()
		{
			return new Configuration() ;
		}

		public void Clear()
		{
			string[] propertyKeys = new string[ this.mProperties.Count];
			this.mProperties.Keys.CopyTo( propertyKeys, 0);
			propertyKeys.Apply( item => this.DeleteProperty(item));
		}

		public Boolean HasProperty(String name)
		{
			return this.mProperties.Contains( name);
		}

		public Object GetProperty (String name)
		{
			return this.GetProperty(name, null);
		}
		public Object GetProperty(String name, Object @default)
		{
			if ( this.mProperties.Contains( name))
			{
				return this.mProperties[ name];
			}
			return @default;
		}

		public T GetProperty<T>( String name)
		{
			return GetProperty<T>( name, default( T));
		}
		
		public T GetProperty<T>( String name, T @default)
		{
			Type type = typeof(T);
			object obj = GetProperty(name, @default as object);

			if ( obj == null)
			{
				return @default;
			}
			if ( obj.GetType() == type || obj.GetType().IsSubclassOf(type))
			{
				return (T)obj;
			}
			throw new InvalidCastException("The property does not match the specify type");
		}

		public IConfiguration SetProperty( String name, Object value)
		{
			return this.SetProperty( name, value, null);
		}
		public IConfiguration SetProperty(String name, Object value, Object @default)
		{
			if ( (value == null) || object.Equals(value, @default))
			{
				this.DeleteProperty( name);
			}
			else if (!this.mProperties.Contains(name) || !object.Equals(this.mProperties[name], value))
			{
				this.mProperties[name] = value;
				this.OnPropertyChanged( new PropertyChangedEventArgs(name));
			}
			return this;
		}

		public void DeleteProperty(string name)
		{
			if ( this.mProperties.Contains(name))
			{
				this.mProperties.Remove(name);
				this.OnPropertyChanged(new PropertyChangedEventArgs(name));
			}
		}


		public void LoadProperty<T>( T t, params Expression<Func<T, Object>>[] props)
		{
			if ( props != null)
			{
				foreach (var p in props)
				{
					string name = p.GetMemberInfo().Name;
					if ( HasProperty(name))
					{
						PropertyExtension.SetProperty( t, name, GetProperty(name));
					}
				}
			}
		}
		public void SaveProperty<T>( T t, params Expression<Func<T, Object>>[] props)
		{
			if (props != null)
			{
				foreach (var p in props)
				{
					string name = p.GetMemberInfo().Name;
					SetProperty(name, PropertyExtension.GetProperty( t, name));
				}
			}
		}


		public event PropertyChangedEventHandler PropertyChanged;

		protected void OnPropertyChanged( PropertyChangedEventArgs e)
		{
			if (this.PropertyChanged != null)
			{
				this.PropertyChanged( this, e);
			}
		}

	}


	internal static class ConfigurationHelper
	{
		internal static void Load( this Configuration configuration, XElement element)
		{
			foreach (var e in element.Elements( ConfigurationPersistentManager.CONFIG_PROPERTY))
			{
				string name = e.Attribute(XName.Get( ConfigurationPersistentManager.CONFIG_NAME)).Value;
				var propType = e.Attribute(XName.Get(ConfigurationPersistentManager.CONFIG_TYPE));

				Type type ;
				if (propType != null && !string.IsNullOrEmpty(propType.Value))
				{
					type = Type.GetType(propType.Value);
				}
				else
				{
 					type = typeof ( string);
				}
				TypeConverter converter = TypeDescriptor.GetConverter(type);
				if (converter != null)
				{
					try
					{
						object o = converter.ConvertFromString(null, CultureInfo.InvariantCulture, e.Value);
						configuration.SetProperty( name, o);
					}
					catch (Exception except )
					{
						Configuration.mLog.Error( except);
					}
				}
			}
		}

		internal static void Save( this Configuration configuration, XElement element)
		{
			foreach ( var key in configuration.mProperties.Keys)
			{
				object obj = configuration.mProperties[ key];
				Configuration c = obj as Configuration;
				if (c != null)
				{
					c.Save( element);
				}
				else
				{
					XElement e = new XElement( ConfigurationPersistentManager.CONFIG_PROPERTY);
					e.SetAttributeValue( ConfigurationPersistentManager.CONFIG_NAME, key);

					Type type = obj.GetType();
					if ( type != typeof(string))
					{
						e.SetAttributeValue( ConfigurationPersistentManager.CONFIG_TYPE, type.AssemblyQualifiedName);
					}
					string text = TypeDescriptor.GetConverter(type)
												.ConvertToString(null, CultureInfo.InvariantCulture, obj);
					e.Value = text;
					element.Add( e);
				}
			}
		}

	}
}
